package com.yeyks.app.controller.base.consumer;

import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.json.JSONUtil;
import com.yeyks.common.ajax.AjaxResponseBody;
import com.yeyks.common.exception.ServiceException;
import com.yeyks.common.param.security.SecurityDataParam;
import com.yeyks.common.utils.AppServerUtil;
import com.yeyks.common.utils.ArgumentsCheckUtil;
import com.yeyks.common.utils.TextValidator;
import com.yeyks.common.utils.security.AppServerAESUtil;
import com.yeyks.commonReference.append.app.OssService;
import com.yeyks.commonReference.append.sms.SmsService;
import com.yeyks.commonReference.append.sms.impl.SmsManage;
import com.yeyks.consumer.dal.domain.UserInfo;
import com.yeyks.consumer.dto.BankBindDTO;
import com.yeyks.consumer.param.UserInfo.UserInfoLoginParam;
import com.yeyks.consumer.param.UserInfo.UserInfoOneClickLoginParam;
import com.yeyks.consumer.param.UserInfo.UserInfoPhoneLoginParam;
import com.yeyks.consumer.param.UserInfo.UserInofCheckCodeParam;
import com.yeyks.consumer.service.UserInfoService;
import com.yeyks.consumer.vo.UserInfoLoginVo;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * @Author fuchangshuai
 * @date 2019/3/26 3:12 PM
 */
@Api(tags = "客户端用户接口", description = "用户")
@Slf4j
@RestController("appUserInfoController")
@RequestMapping(value = "/app/user")
public class UserInfoController {

    @Autowired
    private UserInfoService userInfoService;
    @Autowired
    private SmsService smsService;
    @Autowired
    private SmsManage smsManage;
    @Autowired
    private OssService ossService;

    /**
     * 通过原始名称获取随机名称的图片名
     */
    private static String getFileName(String ext) {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String name = simpleDateFormat.format(date);
        return name + getSixNumStr() + "." + ext;
    }

    /**
     * 获取六位数字随机数
     */
    private static String getSixNumStr() {
        Random random = new Random();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }

    /**
     * 获得短信验证码用于注册
     *
     * @param phone 需要创建账号的电话号码
     * @return
     */
    @ApiOperation(value = "获得短信验证用于注册")
    @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String")
    @RequestMapping(value = "/getVerifyCode", method = RequestMethod.POST)
    public AjaxResponseBody getVerifyCode(@RequestParam(name = "phone") String phone) {
        checkParamPhone(phone);
        return sendMessage(phone);
    }

    /**
     * 验证发送的注册短信是否正确
     *
     * @param phone
     * @param code
     * @return
     */
    @ApiOperation(value = "验证发送的注册短信是否正确")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "code", value = "验证码", dataType = "String")
    })
    @RequestMapping(value = "/verifyCode", method = RequestMethod.POST)
    public AjaxResponseBody verifyCode(@RequestParam(name = "phone") String phone,
                                       @RequestParam(name = "code") String code) {
        boolean flag = smsService.verifySmsForSignup(phone, code);
        if (flag) {
            return AjaxResponseBody.success(true);
        }
        return AjaxResponseBody.failed(-1, "验证码有误");
    }

    /**
     * 忘记密码发送验证码
     *
     * @param phone
     * @return
     */
    @ApiOperation(value = "忘记密码发送验证码")
    @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String")
    @PostMapping(value = "/forgetPwdGetCode")
    public AjaxResponseBody getPasswordVerifyCode(@RequestParam(name = "phone") String phone) {
        checkParamPhone(phone);
        UserInfo userInfo = userInfoService.selectUseInfoPhone(phone);
        if (userInfo == null) {
            return AjaxResponseBody.failed(-19950910, "该用户不存在");
        }
        return sendMessage(phone);
    }

    @ApiOperation(value = "用户登陆v3.7.3", notes = "使用手机号验证码或者手机号密码登陆")
    @PostMapping(value = "/login")
    public AjaxResponseBody<UserInfoLoginVo> loginV2(UserInfoLoginParam param, HttpServletRequest request) {
        param.setIp(ServletUtil.getClientIP(request));
        return AjaxResponseBody.success(userInfoService.login(param));
    }

    @ApiOperation(value = "用户登陆v3.7.3", notes = "使用手机号验证码或者手机号密码登陆。参数同用户登陆接口")
    @PostMapping(value = "/app/login")
    public AjaxResponseBody<String> loginApp(SecurityDataParam param, HttpServletRequest request) {
        UserInfoLoginParam userInfoLoginParam = JSONUtil.toBean(AppServerAESUtil.CBC.decryptQuietly(param.getData()), UserInfoLoginParam.class);
        userInfoLoginParam.setIp(ServletUtil.getClientIP(request));
        UserInfoLoginVo userInfoLoginVo = userInfoService.loginApp(userInfoLoginParam);
        return AjaxResponseBody.success(AppServerAESUtil.CBC.encryptQuietly(JSONUtil.toJsonStr(userInfoLoginVo)));
    }

    @ApiOperation(value = "一键登录:v4.1.0", notes = "一键登录")
    @PostMapping(value = "/one/click/login")
    public AjaxResponseBody<UserInfoLoginVo> oneClickLogin(@Validated UserInfoOneClickLoginParam param, HttpServletRequest request) {
        param.setIp(ServletUtil.getClientIP(request));
        return AjaxResponseBody.success(userInfoService.oneClickLogin(param));
    }

    @ApiOperation(value = "分享后用户通过手机号注册:v3.5", notes = "分享后用户通过手机号注册")
    @PostMapping(value = "/phone/login")
    public AjaxResponseBody phoneLogin(UserInfoPhoneLoginParam param) {
        userInfoService.phoneLogin(param);
        return AjaxResponseBody.success();
    }

    /**
     * 推广活动中发送验证码使用
     *
     * @param phone
     * @return
     */
//    @ApiIgnore
//    @ApiOperation(value = "推广活动中发送验证码使用", notes = "推广活动中发送验证码使用")
//    @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String")
//    @PostMapping(value = "/recommend/login")
    public AjaxResponseBody recommendLogin(@RequestParam(name = "phone") String phone) {
        checkParamPhone(phone);
        sendMessage(phone);
        return AjaxResponseBody.success();
    }

    //推广活动验证验证码是否正确
    @ApiOperation(value = "推广活动验证验证码是否正确:v3.5", notes = "推广活动验证验证码是否正确")
    @PostMapping(value = "/recommend/check/code")
    public AjaxResponseBody<String> checkCode(@Validated UserInofCheckCodeParam param) {
        return AjaxResponseBody.success(userInfoService.checkCode(param));
    }

    /**
     * 判断当前用户是否存在
     *
     * @param phone
     * @return
     */
    @ApiOperation(value = "判断当前用户是否存在", notes = "通过手机号查询该用户是否存在")
    @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String")
    @PostMapping(value = "/userInfo")
    public AjaxResponseBody<Boolean> userInfo(@RequestParam(name = "phone") String phone) {
        log.info("获取当前用户手机号{}", phone);
        checkParamPhone(phone);
        UserInfo operatorInfo = userInfoService.selectUseInfoPhone(phone);
        if (operatorInfo == null) {
            return AjaxResponseBody.success(false);
        }
        return AjaxResponseBody.success(true);
    }

    /**
     * 用户设置密码或忘记密码
     *
     * @param token
     * @param phone
     * @param password
     * @return
     */
    @ApiOperation(value = "用户设置密码或忘记密码:v3.3", notes = "设置密码传入token和密码，忘记密码需先验证手机号，再传手机号和密码")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "token", value = "token", dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "phone", value = "手机号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "password", value = "密码", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "code", value = "验证码", required = false, dataType = "String")
    })
    @PostMapping(value = "/setPwd")
    public AjaxResponseBody userSetPwd(@RequestParam(name = "token", required = false) String token,
                                       @RequestParam(name = "phone") String phone,
                                       @RequestParam(name = "password") String password,
                                       @RequestParam(name = "code", required = false) String code
    ) {
        checkParamPhonePwd(phone, password);
        //加密密码
//        password = AppServerUtil.getEncodePassword(password);
        if (ArgumentsCheckUtil.isEmpty(token)) {//忘记密码，参数手机号和密码和验证码
            //校验验证码
            if (!smsManage.checkCode(phone, code)) {
                throw new ServiceException("验证码错误");
            }
            UserInfo userInfo = userInfoService.selectUseInfoPhone(phone);
            int rec = userInfoService.modifyPasswordById(userInfo.getId(), password);
            //修改MD5密码
            userInfoService.updateById(new UserInfo().setId(userInfo.getId()).setMd5Password(AppServerUtil.getEncodePassword(password)));
            if (rec == 0) {
                throw new ServiceException("修改数据失败");
            }
            return AjaxResponseBody.success();
        }
        //第一次设置密码，token和密码
        UserInfo operatorInfo = userInfoService.tokenUserInfo(token);
        if (!operatorInfo.getPhone().equals(phone)) {
            throw new ServiceException("拒绝非本人操作");
        }
        int rec = userInfoService.modifyPasswordById(operatorInfo.getId(), password);
        //修改MD5密码
        userInfoService.updateById(new UserInfo().setId(operatorInfo.getId()).setMd5Password(AppServerUtil.getEncodePassword(password)));
        if (rec == 0) {
            throw new ServiceException("修改数据失败");
        }
        return AjaxResponseBody.success();
    }

    /**
     * 用户修改密码
     *
     * @param token
     * @param origPwd
     * @param id
     * @param password
     * @return
     */
    @ApiOperation(value = "用户修改密码", notes = "注意验证两次密码输入是否一致")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "token", value = "token", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "origPwd", value = "原始密码", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "id", value = "用户id", required = true, dataType = "int"),
            @ApiImplicitParam(paramType = "query", name = "password", value = "新密码", required = true, dataType = "String")
    })
    @PostMapping(value = "/modifyPassword")
    public AjaxResponseBody modifyPassword(@RequestParam(name = "token") String token,
                                           @RequestParam(name = "origPwd") String origPwd,
                                           @RequestParam(name = "id") Integer id,
                                           @RequestParam(name = "password") String password) {

        checkParamPwd(password);
        UserInfo operatorInfo = userInfoService.tokenUserInfo(token);
        if (!operatorInfo.getId().equals(id)) {
            throw new ServiceException("当前操作人非本人");
        }
        //加密密码
//        origPwd = AppServerUtil.getEncodePassword(origPwd);
//        password = AppServerUtil.getEncodePassword(password);
        UserInfo origAccountInfo = userInfoService.getById(id);
        if (!AppServerUtil.getEncodePassword(origPwd).equals(origAccountInfo.getMd5Password())) {
            throw new ServiceException("原密码输入有误");
        }

        int rec = userInfoService.modifyPasswordById(id, password);
        //修改MD5密码
        userInfoService.updateById(new UserInfo().setId(operatorInfo.getId()).setMd5Password(AppServerUtil.getEncodePassword(password)));
        if (rec == 0) {
            throw new ServiceException("更新密码失败");
        }

        return AjaxResponseBody.success();
    }

    /**
     * 用户上传头像
     *
     * @return
     */
    @ApiOperation(value = "用户上传头像", notes = "用户上传头像")
    @PostMapping(value = "/upload/avatar")
    public AjaxResponseBody<Map<String, Object>> uploadAvatar(@ApiParam(value = "token", name = "token", required = true) @RequestParam(name = "token") String token,
                                                              @ApiParam(value = "上传的文件", name = "file", required = true) @RequestParam(name = "file") MultipartFile file) {
        UserInfo userInfo = userInfoService.tokenUserInfo(token);
        Map<String, Object> map = ossUpload(userInfo.getId(), file);
        if (map.size() == 0) {
            throw new ServiceException("文件上传失败");
        }
        String url = (String) map.get("url");
        Integer res = userInfoService.updateAvatar(userInfo.getId(), url);
        if (res != 1) {
            throw new ServiceException("更新用户头像失败");
        }
        return AjaxResponseBody.success(map);
    }

    /**
     * 用户绑定银行卡
     *
     * @return
     */
    @ApiOperation(value = "用户绑定或者修改银行卡", notes = "用户绑定或者修改银行卡")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "query", name = "token", value = "token", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "bankCard", value = "银行卡号", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "bankName", value = "银行名称", required = true, dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "branchName", value = "支行名称", dataType = "String"),
            @ApiImplicitParam(paramType = "query", name = "userName", value = "用户真实名称", required = true, dataType = "String")
    })
    @PostMapping(value = "/bank/card")
    public AjaxResponseBody<Boolean> bankCard(@RequestParam(name = "token") String token,
                                              @RequestParam(name = "bankCard") String bankCard,
                                              @RequestParam(name = "bankName") String bankName,
                                              @RequestParam(name = "branchName", required = false) String branchName,
                                              @RequestParam(name = "userName") String userName) {
        checkParamBank(token, bankCard, bankName, userName);
        UserInfo userInfo = userInfoService.tokenUserInfo(token);
        Boolean flag = userInfoService.bankCard(userInfo, bankCard, bankName, branchName, userName);
        return AjaxResponseBody.success(flag);
    }

    /**
     * 获取用户绑卡信息
     *
     * @return
     */
    @ApiOperation(value = "获取用户绑卡信息", notes = "获取用户绑卡信息", response = BankBindDTO.class)
    @ApiImplicitParam(paramType = "query", name = "token", value = "token", required = true, dataType = "String")
    @PostMapping(value = "/bank/card/info")
    public AjaxResponseBody<BankBindDTO> bankCardInfo(@RequestParam(name = "token") String token) {
        UserInfo userInfo = userInfoService.tokenUserInfo(token);
        return AjaxResponseBody.success(userInfoService.bankCardInfo(userInfo));
    }

    private void checkParamBank(String token, String bankCard, String bankName, String userName) {
        if (!ArgumentsCheckUtil.notEmpty(token)) {
            throw new ServiceException("token不能为空");
        }
        if (!ArgumentsCheckUtil.notEmpty(bankCard)) {
            throw new ServiceException("银行卡号输入为空");
        }
        if (!TextValidator.ID.boolCheck(bankCard)) {
            throw new ServiceException("银行卡号只能是数字");
        }
        if (bankCard.length() < 16 || bankCard.length() > 19) {
            throw new ServiceException("请输入正确的银行卡号");
        }
        if (!ArgumentsCheckUtil.notEmpty(bankName)) {
            throw new ServiceException("营养名称输入为空");
        }
        if (!ArgumentsCheckUtil.notEmpty(userName)) {
            throw new ServiceException("用户真实姓名输入为空");
        }
    }

    private Map<String, Object> ossUpload(Integer userId, MultipartFile file) {

        Map<String, Object> result = new HashMap<>();
        try {
            if (!file.isEmpty()) {
                String filename = file.getOriginalFilename();
                String newName = getFileName(FilenameUtils.getExtension(filename));
                String ossKey = "User/" + userId.toString() + "/" + newName;
                if (ossService.writeOSS(file.getBytes(), ossKey)) {
                    result.put("url", ossService.getPrefixUrl() + ossKey);
                    return result;
                }
            }
        } catch (Exception e) {
            log.info("文件上传出错{}", e.getLocalizedMessage());
            return result;
        }
        return result;
    }

    private AjaxResponseBody sendMessage(String phone) {
        try {
            smsService.sendSmsForSignup(phone);
            return AjaxResponseBody.success(true);
//        } catch (SmsSendException e) {
//            if (e.getWait() == 0) {
//                throw new ServiceException("短信服务器内部错误");
//            } else {
//                throw new ServiceException("一分钟限发一条");
//            }
        } catch (ServiceException e) {
            // 其它写入失败
            return AjaxResponseBody.failed(-1, e.getMessage());
        } catch (Throwable e) {
            // 其它写入失败
            return AjaxResponseBody.failed(-1, "fail");
        }
    }

    private void checkParamPhone(String phone) {
        if (ArgumentsCheckUtil.isEmpty(phone)) {
            throw new ServiceException("手机号不能为空");
        }
        if (!TextValidator.MOBILE.boolCheck(phone)) {
            throw new ServiceException("手机号格式非法");
        }
    }

    private void checkParamPwd(String password) {
        if (ArgumentsCheckUtil.isEmpty(password)) {
            throw new ServiceException("密码输入为空");
        }
        if (password.length() < 6 || password.length() > 16) {
            throw new ServiceException("密码的有效长度为6-16位");
        }
    }

    private void checkParamPhoneUseId(String phone, Integer userId) {
        checkParamPhone(phone);
        if (ArgumentsCheckUtil.isNullOrZero(userId)) {
            throw new ServiceException("分享人id不能为空");
        }
    }

    private void checkParamPhonePwd(String phone, String password) {
        checkParamPhone(phone);
        checkParamPwd(password);
    }

}
